package com.tianji.learning.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.cache.CategoryCache;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CategoryClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.client.remark.RemarkClient;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constant.RedisConstnt2;
import com.tianji.learning.domain.dto.NoteDTO;
import com.tianji.learning.domain.po.Note;
import com.tianji.learning.domain.query.AdminNoteQuery;
import com.tianji.learning.domain.query.NoteQuery;
import com.tianji.learning.domain.vo.AdminNotePageVO;
import com.tianji.learning.domain.vo.NoteDetailVO;
import com.tianji.learning.domain.vo.NotePageVO;
import com.tianji.learning.mapper.NoteMapper;
import com.tianji.learning.service.INoteService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 笔记记录表 服务实现类
 * </p>
 *
 * @author hrz
 * @since 2025-09-04
 */
@Service
@RequiredArgsConstructor
public class NoteServiceImpl extends ServiceImpl<NoteMapper, Note> implements INoteService {

    private final RemarkClient remarkClient;
    private final StringRedisTemplate stringRedisTemplate;
    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final CategoryClient categoryClient;
    private final UserClient userClient;

    @Override
    public void addNote(NoteDTO noteDTO) {
        //1.转换为实体
        Note note = BeanUtil.copyProperties(noteDTO, Note.class);
        //2.新增
        save(note);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void gatherNote(Long id) {
        //1.根据笔记id查询出笔记内容
        Note note = getById(id);
        if(note == null){
            return;
        }
        //2.创建一个全新笔记
        Note note1 = BeanUtils.copyBean(note, Note.class);
        note1.setId(null);
        note1.setIsPrivate(true);
        note1.setIsGathered(true);
        note1.setAuthorId(UserContext.getUser());
        note1.setGatheredId(id);
        //3.更新被采集笔记的被采集次数
        note.setUsedTimes(note.getUsedTimes() + 1);
        boolean b = updateById(note);
        //4.保存到数据库
        boolean b1 = save(note1);
        if(!b1 || !b){
            throw new RuntimeException("采集失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteGatheredNote(Long id) {
        //1.根据id查询出采集的笔记
        Note note = getById(id);
        if(note == null){
            return;
        }
        //2.判断作者是否是自己
        if(!note.getAuthorId().equals(UserContext.getUser())){
            return;
        }
        //3.更新被采集笔记的被采集次数
        note.setUsedTimes(note.getUsedTimes() - 1);
        boolean b = updateById(note);
        //4.删除
        boolean b1 = removeById(id);
        if(!b1 || !b){
            throw new RuntimeException("删除失败");
        }
    }

    @Override
    public void updateNote(Long id, NoteDTO noteDTO) {
        //1.查询出笔记
        Note note = getById(id);
        if(note == null){
            return;
        }
        //2.比较作者是否是自己
        if(!note.getAuthorId().equals(UserContext.getUser())){
            return;
        }
        //3.更新字段
        BeanUtil.copyProperties(noteDTO, note);
        //4.更新
        updateById(note);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMyNote(Long id) {
        //1.根据id查询出笔记
        Note note = getById(id);
        if(note == null){
            return;
        }
        //2.比较作者是否是自己
        if(!note.getAuthorId().equals(UserContext.getUser())){
            return;
        }
        //3.删除
        removeById(id);
        //4.删除redis和数据库中的点赞记录
        remarkClient.deleteLiked(id);
        //5.删除redis中的点赞记录
        String key = RedisConstnt2.LIKE_BIZ_KEY_PREFIX + id;
        stringRedisTemplate.delete(key);
    }

    @Override
    public PageDTO<NotePageVO> getNote(NoteQuery noteQuery) {
        Long courseId = noteQuery.getCourseId();
        Long sectionId = noteQuery.getSectionId();
        Boolean onlyMine = noteQuery.getOnlyMine();
        Page<Note> page = this.lambdaQuery()
                .eq(courseId != null, Note::getCourseId, courseId)
                .eq(sectionId != null, Note::getSectionId, sectionId)
                .eq(onlyMine != null, Note::getAuthorId, UserContext.getUser())
                .page(noteQuery.toMpPageDefaultSortByCreateTimeDesc());
        List<Note> records = page.getRecords();
        if(records.isEmpty()){
            return PageDTO.empty(0L,0L);
        }
        List<NotePageVO> notePageVOS = BeanUtil.copyToList(records, NotePageVO.class);
        return PageDTO.of(page, notePageVOS);
    }

    @Override
    public PageDTO<AdminNotePageVO> getAdminNotePage(AdminNoteQuery query) {
        Boolean hidden = query.getHidden();
        LocalDateTime endTime = query.getEndTime();
        LocalDateTime beginTime = query.getBeginTime();
        String name = query.getName();
        List<Note> Result = new ArrayList<>();
        List<AdminNotePageVO> adminNotePageVOS = new ArrayList<>();
        //1.先筛选出目前可获得的所有笔记内容
        Page<Note> page = this.lambdaQuery()
                .eq(hidden != null, Note::getHidden, hidden)
                .between(beginTime != null && endTime != null,
                        Note::getCreateTime, beginTime, endTime)
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        List<Note> records = page.getRecords();
        //2.判断是否传来了课程名关键字
        Set<Long> courseIds = records.stream()
                .map(Note::getCourseId).collect(Collectors.toSet());
        Map<Long,String> m = new HashMap<>();
        Map<Long, Note> m2 = records.stream()
                .collect(Collectors.toMap(Note::getCourseId, c -> c));
        if(!name.isBlank()){
            //2.1传来了关键字就开始筛选page中不符合要求的
            List<CourseSimpleInfoDTO> simpleInfoList =
                    courseClient.getSimpleInfoList(courseIds);
            for (CourseSimpleInfoDTO courseSimpleInfoDTO : simpleInfoList) {
                m.put(courseSimpleInfoDTO.getId(), courseSimpleInfoDTO.getName());
            }
            //2.2过滤出符合关键字的id与名称的集合映射
            m = m.entrySet().stream()
                    .filter(entry -> {
                        return entry.getValue().contains(name);
                    }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            //2.3取交集部分
            m2.keySet().retainAll(m.keySet());
            //2.4取交集部分的NOTE
            for (Long id : m2.keySet()) {
                Result.add(m2.get(id));
            }
            //2.5转VO
            adminNotePageVOS = BeanUtil
                    .copyToList(Result, AdminNotePageVO.class);
        }else{
            //2.6没传课程名关键字就直接转VO
            adminNotePageVOS = BeanUtil
                    .copyToList(records, AdminNotePageVO.class);
        }
        return PageDTO.of(page, adminNotePageVOS);
    }

    @Override
    public void hiddenNote(Long id, Boolean hidden) {
        //1.查询出笔记
        Note note = getById(id);
        if(note == null){
            return;
        }
        //2.修改笔记状态
        note.setHidden(hidden);
        updateById(note);
    }

    @Override
    public NoteDetailVO getNoteDetail(Long id) {
        //1.根据id查询笔记
        Note note = getById(id);
        if(note == null){
            return null;
        }
        NoteDetailVO v = BeanUtils.copyBean(note, NoteDetailVO.class);
        //2.查询课程信息
        CourseFullInfoDTO courseInfoById = courseClient.getCourseInfoById(note.getCourseId(),
                false, false);
        v.setCourseName(courseInfoById.getName());
        //3.查询课程的目录信息
        Long chapterId = note.getChapterId();
        Long sectionId = note.getSectionId();
        List<CataSimpleInfoDTO> c =
                catalogueClient.batchQueryCatalogue(List.of(chapterId));
        List<CataSimpleInfoDTO> s =
                catalogueClient.batchQueryCatalogue(List.of(sectionId));
        v.setChapterName(c.get(0).getName());
        v.setSectionName(s.get(0).getName());
        //4.查询课程分类信息
        List<Long> categoryIds = courseInfoById.getCategoryIds();
        String categoryNameById =
                categoryClient.getCategoryNameById(categoryIds);
        if(StringUtils.isNotBlank(categoryNameById)){
            v.setCategoryNames(categoryNameById);
        }
        //5.查询笔记作者的姓名和电话
        Long authorId = note.getAuthorId();
        UserDTO userDTO = userClient.queryUserById(authorId);
        v.setAuthorName(userDTO.getName());
        v.setAuthorPhone(userDTO.getCellPhone());
        //6.查询该笔记是否被采集
        if(note.getUsedTimes() > 0){
            //6.1被采集过,就去查询采集笔记的人的名称
            List<Note> list = this.lambdaQuery()
                    .eq(Note::getGatheredId, note.getId())
                    .list();
            List<Long> ids = list.stream()
                    .map(Note::getAuthorId).collect(Collectors.toList());
            List<String> names = userClient
                    .queryUserByIds(ids).stream().map(UserDTO::getName)
                    .collect(Collectors.toList());
            v.setGathers(names);
        }else{
            v.setGathers(null);
        }
        return v;
    }

}
